package link.luyu.plugin.brochain;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import link.luyu.protocol.link.Connection;
import okhttp3.*;
import okhttp3.logging.HttpLoggingInterceptor;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BrochainConnection implements Connection {

    private final HttpService service;

    public BrochainConnection(Map<String, Object> properties) {
        Config config = new Config(properties);
        String url = config.getNodeUrl();
        this.service = new HttpService(url);
    }

    @Override
    public void start() throws RuntimeException {}

    @Override
    public void stop() throws RuntimeException {}

    @Override
    public void asyncSend(String path, int type, byte[] data, Callback callback) {
        service.sendAsync(data, callback);
    }

    @Override
    public void subscribe(int type, byte[] data, Callback callback) {}

    private static class HttpService {

        public static final MediaType JSON_MEDIA_TYPE =
                MediaType.parse("application/json; charset=utf-8");

        private static final Logger log = LoggerFactory.getLogger(HttpService.class);

        private final OkHttpClient httpClient;

        private final String url;

        private final HashMap<String, String> headers = new HashMap<>();

        public HttpService(String url, OkHttpClient httpClient) {
            this.url = url;
            this.httpClient = httpClient;
        }

        public HttpService(String url) {
            this(url, createOkHttpClient());
        }

        private static OkHttpClient createOkHttpClient() {
            final OkHttpClient.Builder builder =
                    new OkHttpClient.Builder()
                            .connectionSpecs(
                                    Arrays.asList(
                                            ConnectionSpec.MODERN_TLS,
                                            ConnectionSpec.COMPATIBLE_TLS,
                                            ConnectionSpec.CLEARTEXT));
            configureLogging(builder);
            return builder.build();
        }

        private static void configureLogging(OkHttpClient.Builder builder) {
            if (log.isDebugEnabled()) {
                HttpLoggingInterceptor logging = new HttpLoggingInterceptor(log::debug);
                logging.setLevel(HttpLoggingInterceptor.Level.BODY);
                builder.addInterceptor(logging);
            }
        }

        private void sendAsync(byte[] request, Callback callback) {

            RequestBody requestBody = RequestBody.create(request, JSON_MEDIA_TYPE);
            Headers headers = buildHeaders();

            okhttp3.Request httpRequest =
                    new okhttp3.Request.Builder()
                            .url(url)
                            .headers(headers)
                            .post(requestBody)
                            .build();

            httpClient
                    .newCall(httpRequest)
                    .enqueue(
                            new okhttp3.Callback() {
                                @Override
                                public void onFailure(@NotNull Call call, @NotNull IOException e) {
                                    callback.onResponse(-1, e.getMessage(), null);
                                }

                                @Override
                                public void onResponse(
                                        @NotNull Call call, @NotNull Response response)
                                        throws IOException {
                                    processHeaders(response.headers());
                                    ResponseBody responseBody = response.body();
                                    if (response.isSuccessful()) {
                                        callback.onResponse(
                                                response.code(),
                                                null,
                                                responseBody != null ? responseBody.bytes() : null);
                                    } else {
                                        callback.onResponse(
                                                response.code(),
                                                "Invalid response received",
                                                responseBody != null ? responseBody.bytes() : null);
                                    }
                                }
                            });
        }

        private void processHeaders(Headers headers) {
            // Default implementation is empty
        }

        private Headers buildHeaders() {
            return Headers.of(headers);
        }
    }
}
